package com.tiger.poker.util;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tiger.poker.controller.GoldenFlowers;
import com.tiger.poker.entity.Player;
import com.tiger.poker.enums.PlayerOperationStatusEnum;
import com.tiger.poker.enums.PlayerStatusEnum;
import lombok.AllArgsConstructor;
import lombok.Getter;

import javax.websocket.Session;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import static com.tiger.poker.controller.GoldenFlowers.*;
import static com.tiger.poker.util.PokerUtils.CHIP_FIVE;

/**
 * @author Joy on 2019/2/12 9:38
 */
public class GameUtil {
    private static final int JOIN_GAME = 0;
    private static final int JOIN_SUCCESS = -1;
    private static final int JOIN_FAILED = -2;
    private static final int WATCH_POKER = 1;
    private static final int CALL_CHIP = 2;
    private static final int COMPARE_POKER = 3;
    public static final int SHOW_MESSAGE = 88;
    public static final int GAME_OVER = 89;
    public static final int UPDATE_DATA = 99;
    public static final int RESET_GAME = 100;

    public static final int START_GAME = 1000;
    public static final int CHECK = 1001;
    public static final int LOOK_CARDS = 1002;
    public static final int GIVE_UP = 1003;
    public static final int BATTLE = 1004;

    public static final ObjectMapper MAPPER = new ObjectMapper();

    public static void resolveMessage(int type, String content, Session session) throws JsonProcessingException {
        Player playerCurrent = null;
        for (Player player : PLAYER_SET) {
            if (player.getSession().equals(session)) {
                playerCurrent = player;
                break;
            }
        }
        if (type == JOIN_GAME) {
            joinGame(content, session);
            return;
        }
        if (playerCurrent == null) {
            return;
        }
        if (type == START_GAME) {
            startGame(playerCurrent);
        } else {
            if (playerCurrent.getOperationStatus() == PlayerOperationStatusEnum.DISABLE.getCode()) {
                return;
            }
            switch (type) {

                case CHECK:
                    PokerUtils.check(playerCurrent);
                    changeOperationStatus(PLAYER_SET, playerCurrent);

                    WebSocketUtil.sendMessageBatch(PLAYER_SET, assemble(SHOW_MESSAGE, playerCurrent.getName() + "  跟注了"));
                    break;
                case LOOK_CARDS:
                    PokerUtils.lookCards(playerCurrent);

                    WebSocketUtil.sendMessageBatch(PLAYER_SET, assemble(SHOW_MESSAGE, playerCurrent.getName() + "  看牌了"));
                    break;
                case GIVE_UP:
                    PokerUtils.giveUp(playerCurrent);

                    Tuple<Boolean, Player> isGameOver = isGameOver(PLAYER_SET);
                    if (isGameOver.v1) {
                        WebSocketUtil.sendMessageBatch(PLAYER_SET, assemble(GAME_OVER, isGameOver.v2.getName() + "  胜利了"));
                    } else {
                        changeOperationStatus(PLAYER_SET, playerCurrent);
                    }

                    WebSocketUtil.sendMessageBatch(PLAYER_SET, assemble(SHOW_MESSAGE, playerCurrent.getName() + "  弃牌了"));
                    break;
                case BATTLE:
                    boolean canBattle = PokerUtils.canBattle(PLAYER_SET);
                    if (canBattle) {
                        PokerUtils.check(playerCurrent);
                        Player other = null;
                        for (Player player : PLAYER_SET) {
                            if (content.equals(player.getName())) {
                                other = player;
                                break;
                            }
                        }

                        Player winner = PokerUtils.battle2Death(playerCurrent, other);
                        if (winner.equals(playerCurrent)) {
                            other.setStatus(PlayerStatusEnum.GIVE_UP.getCode());
                        } else {
                            playerCurrent.setStatus(PlayerStatusEnum.GIVE_UP.getCode());
                        }
                        Tuple<Boolean, Player> isGameOver1 = isGameOver(PLAYER_SET);
                        if (isGameOver1.v1) {
                            WebSocketUtil.sendMessageBatch(PLAYER_SET, assemble(GAME_OVER, isGameOver1.v2.getName() + "  胜利了"));
                        } else {
                            changeOperationStatus(PLAYER_SET, playerCurrent);
                        }
                    } else {
                        WebSocketUtil.sendMessageBatch(PLAYER_SET, assemble(SHOW_MESSAGE, playerCurrent.getName() + "  不能比牌"));
                    }


                    break;
                default:
            }
        }


        WebSocketUtil.sendMessageBatch(PLAYER_SET, assemble(UPDATE_DATA, MAPPER.writeValueAsString(PLAYER_SET)));
    }

    private static void startGame(Player playerCurrent) {
        playerCurrent.setStatus(PlayerStatusEnum.READY.getCode());
        if (PLAYER_SET.size() == 1) {
            return;
        }
        for (Player player : PLAYER_SET) {
            if (player.getStatus() != PlayerStatusEnum.READY.getCode()) {
                return;
            }
        }
        PokerUtils.dealtCard(PLAYER_SET);


        for (Player player : PLAYER_SET) {
            player.setStatus(PlayerStatusEnum.NOT_LOOK.getCode());
            player.setScore(player.getScore() - CHIP_FIVE);
            score += CHIP_FIVE;
        }

        for (Player player : PLAYER_SET) {
            player.setOperationStatus(PlayerOperationStatusEnum.ENABLE.getCode());
            break;
        }

        WebSocketUtil.sendMessageBatch(PLAYER_SET, assemble(SHOW_MESSAGE, playerCurrent.getName() + "  准备中"));
    }


    public static String assemble(int type, String message) {
        return type + MESSAGE_SEPARATOR + message + MESSAGE_SEPARATOR + score;
    }


    private static void joinGame(String name, Session session) {
        Player player = new Player(session);
        player.setName(name);
        boolean nameExist = PLAYER_SET.stream().
                anyMatch(v -> player.getName().equals(v.getName()));
        if (nameExist) {
            WebSocketUtil.sendMessage(session, JOIN_FAILED + GoldenFlowers.MESSAGE_SEPARATOR);
        } else {
            WebSocketUtil.sendMessage(session, JOIN_SUCCESS + GoldenFlowers.MESSAGE_SEPARATOR);
            WebSocketUtil.sendMessageBatch(PLAYER_SET,
                    SHOW_MESSAGE + GoldenFlowers.MESSAGE_SEPARATOR + player.getName() + " 加入了游戏");
            player.setScore(200);
            player.setStatus(PlayerStatusEnum.JOIN_GAME.getCode());
            PLAYER_SET.add(player);
        }
    }

    public static void changeOperationStatus(Set<Player> players, Player operatePlayer) {
        List<Player> playerList = new ArrayList<>(players);

        int index = playerList.indexOf(operatePlayer);

        operatePlayer.setOperationStatus(PlayerOperationStatusEnum.DISABLE.getCode());


        //最多只需要找5次,不可能有超过5个人的,避免出现死循环
        for (int i = 0; i < 5; i++) {
            if (index == playerList.size() - 1) {
                index = 0;
            } else {
                index++;
            }

            if (playerList.get(index).getStatus() == PlayerStatusEnum.NOT_LOOK.getCode()
                    || playerList.get(index).getStatus() == PlayerStatusEnum.LOOKED.getCode()) {
                playerList.get(index).setOperationStatus(PlayerOperationStatusEnum.ENABLE.getCode());
                break;
            }
        }

    }

    private static Tuple<Boolean, Player> isGameOver(Set<Player> players) {
        int count = 0;
        Player winner = null;
        for (Player player : players) {
            if (player.getStatus() == PlayerStatusEnum.NOT_LOOK.getCode()
                    || player.getStatus() == PlayerStatusEnum.LOOKED.getCode()) {
                count++;
                winner = player;
            }
        }

        if (count <= 1) {
            if (winner == null){
                winner = PLAYER_SET.stream().findAny().orElse(new Player(null));
            }
            winner.setScore(winner.getScore() + score);
            score = 0;
            PLAYER_SET.forEach(p -> p.setStatus(PlayerStatusEnum.GAME_OVER.getCode()));
            return new Tuple<>(true, winner);
        } else {
            return new Tuple<>(false, winner);
        }

    }

    @Getter
    @AllArgsConstructor
    private static class Tuple<V1, V2> {
        private V1 v1;
        private V2 v2;

    }

}
